Page({
  data: {
    cellArr: [],
    isGameOver: false,
    score: 0,
    highestScore: 0,
    history: [],
    historyIndex: -1,
    isBombMode: false,
    showScoreAnimation: false,
    animationPoints: 0
  },
  onLoad: function () {
    const highestScore = wx.getStorageSync('highestScore') || 0;
    this.setData({
      highestScore: highestScore
    });
    this.startGame();
  },
  startGame() {
    this.setData({
      isGameOver: false,
      cellArr: Array.from(Array(4)).map(() => Array(4).fill(0)),
      score: 0,
      history: [],
      historyIndex: -1,
      isBombMode: false
    });
    this.fillOneEmptyCell();
    this.fillOneEmptyCell();
  },
  fillOneEmptyCell() {
    let cellIndex = this.findOneEmptyCell();
    if (cellIndex != -1) {
      let row = Math.floor(cellIndex / 4);
      let col = cellIndex % 4;
      let cellArr = this.data.cellArr;
      cellArr[row][col] = this.getRandomValue();
      this.setData({
        cellArr: cellArr
      });
    }
  },
  getRandomValue() {
    return Math.random() < 0.9 ? 2 : 4;
  },
  findOneEmptyCell() {
    let cells = [];
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) {
        if (this.data.cellArr[i][j] == 0) {
          cells.push(i * 4 + j);
        }
      }
    }
    if (cells.length) {
      return cells[Math.floor(Math.random() * cells.length)];
    } else {
      return -1;
    }
  },
  canMove() {
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) {
        let cell = this.data.cellArr[i][j];
        if (cell) {
          if (j < 3 && this.data.cellArr[i][j] == this.data.cellArr[i][j + 1]) {
            return true;
          }
          if (i < 3 && this.data.cellArr[i][j] == this.data.cellArr[i + 1][j]) {
            return true;
          }
        } else {
          return true;
        }
      }
    }
    return false;
  },
  horizontalMoveCells(toLeft) {
    for (let i = 0; i < 4; i++) {
      let newArr = Array(4).fill(0);
      for (let j = 0; j < 4; j++) {
        newArr[j] = this.data.cellArr[i][j];
      }
      let resultArr = this.removeZerosAndAdd(newArr, toLeft);
      for (let j = 0; j < 4; j++) {
        this.data.cellArr[i][j] = resultArr[j];
      }
    }
  },
  verticalMoveCells(toUp) {
    for (let i = 0; i < 4; i++) {
      let newArr = Array(4).fill(0);
      for (let j = 0; j < 4; j++) {
        newArr[j] = this.data.cellArr[j][i];
      }
      let resultArr = this.removeZerosAndAdd(newArr, toUp);
      for (let j = 0; j < 4; j++) {
        this.data.cellArr[j][i] = resultArr[j];
      }
    }
  },
  removeZerosAndAdd(arr, toHead) {
    let newArr = Array(4).fill(0);
    let arrWithoutZero = arr.filter((x) => x !== 0);
    if (arrWithoutZero.length == 0) {
      return newArr;
    }
    if (toHead) {
      for (let i = 0; i < arrWithoutZero.length; i++) {
        newArr[i] = arrWithoutZero[i];
      }
      for (let i = 0; i < newArr.length - 1; i++) {
        if (newArr[3 - i] === newArr[2 - i] && newArr[3 - i] !== 0) {
          newArr[3 - i] = 0;
          newArr[2 - i] *= 2;
          this.updateScore(newArr[2 - i]);
        }
      }
    } else {
      for (let i = 0; i < arrWithoutZero.length; i++) {
        newArr[newArr.length - i - 1] =
          arrWithoutZero[arrWithoutZero.length - i - 1];
      }
      for (let i = 0; i < newArr.length - 1; i++) {
        if (newArr[i] === newArr[i + 1] && newArr[i] !== 0) {
          newArr[i] = 0;
          newArr[i + 1] *= 2;
          this.updateScore(newArr[i + 1]);
        }
      }
    }
    return newArr;
  },
  updateScore(points) {
    const newScore = this.data.score + points;
    this.setData({
      score: newScore,
      showScoreAnimation: true,
      animationPoints: points
    });
    setTimeout(() => {
      this.setData({
        showScoreAnimation: false
      });
    }, 1000);
    if (newScore > this.data.highestScore) {
      this.setData({
        highestScore: newScore
      });
      wx.setStorageSync('highestScore', newScore);
    }
  },
  moveUp() {
    this.verticalMoveCells(true);
    this.checkGameOverOrContinue();
    this.saveHistory();
  },
  moveDown() {
    this.verticalMoveCells(false);
    this.checkGameOverOrContinue();
    this.saveHistory();
  },
  moveLeft() {
    this.horizontalMoveCells(true);
    this.checkGameOverOrContinue();
    this.saveHistory();
  },
  moveRight() {
    this.horizontalMoveCells(false);
    this.checkGameOverOrContinue();
    this.saveHistory();
  },
  checkGameOverOrContinue() {
    if (this.canMove()) {
      this.fillOneEmptyCell();
    } else {
      this.setData({
        isGameOver: true
      });
    }
  },
  touchStartX: 0,
  touchStartY: 0,
  touchEndX: 0,
  touchEndY: 0,
  touchStart: function (ev) {
    var touch = ev.touches[0];
    this.touchStartX = touch.clientX;
    this.touchStartY = touch.clientY;
    this.touchEndX = touch.clientX;
    this.touchEndY = touch.clientY;
  },
  touchMove: function (ev) {
    var touch = ev.touches[0];
    this.touchEndX = touch.clientX;
    this.touchEndY = touch.clientY;
  },
  touchEnd: function () {
    const disX = this.touchStartX - this.touchEndX;
    const absdisX = Math.abs(disX);
    const disY = this.touchStartY - this.touchEndY;
    const absdisY = Math.abs(disY);
    if (this.data.isBombMode) {
      this.handleBomb();
      this.setData({
        isBombMode: false
      });
    } else if (Math.max(absdisX, absdisY) > 10) {
      const direction = absdisX > absdisY ? (disX < 0 ? "right" : "left") : (disY < 0 ? "down" : "up");
      if (direction == "up") {
        this.moveUp();
      } else if (direction == "down") {
        this.moveDown();
      } else if (direction == "left") {
        this.moveLeft();
      } else if (direction == "right") {
        this.moveRight();
      }
    }
  },
  handleBomb() {
    const touchX = this.touchEndX;
    const touchY = this.touchEndY;
    const query = wx.createSelectorQuery().in(this);
    query.select('.box').boundingClientRect((rect) => {
      if (rect) {
        const cellWidth = rect.width / 4;
        const cellHeight = rect.height / 4;
        const row = Math.floor((touchY - rect.top) / cellHeight);
        const col = Math.floor((touchX - rect.left) / cellWidth);
        if (row >= 0 && row < 4 && col >= 0 && col < 4) {
          let cellArr = this.data.cellArr;
          if (cellArr[row][col] !== 0) {
            cellArr[row][col] = 0;
            this.setData({
              cellArr: cellArr
            });
          }
        }
      }
    }).exec();
  },
  saveHistory() {
    let history = this.data.history;
    let historyIndex = this.data.historyIndex;
    if (historyIndex < history.length - 1) {
      history = history.slice(0, historyIndex + 1);
    }
    history.push({
      cellArr: JSON.parse(JSON.stringify(this.data.cellArr)),
      score: this.data.score
    });
    if (history.length > 10) {
      history.shift();
    }
    this.setData({
      history: history,
      historyIndex: history.length - 1
    });
  },
  undo() {
    let historyIndex = this.data.historyIndex;
    if (historyIndex > 0) {
      historyIndex--;
      let history = this.data.history;
      this.setData({
        cellArr: JSON.parse(JSON.stringify(history[historyIndex].cellArr)),
        score: history[historyIndex].score,
        historyIndex: historyIndex
      });
    }
  },
  activateBombMode() {
    this.setData({
      isBombMode: true
    });
  }
});